using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Net;
using Rest.Xml;
using Rest.Transport;
using Rest.Http;

namespace Rest.Service
{
    /// <summary>
    /// Base class representing a proxy for a REST service
    /// </summary>
    public class RestServiceProxyBase
    {
        #region Properties

        /// <summary>
        /// 
        /// </summary>
        private IRestServiceXmlSchema restServiceXmlSchema;
        private Uri serviceUrl;

        /// <summary>
        /// The IRestServiceXmlValidator associated with the current instance
        /// </summary>
        public IRestServiceXmlSchema RestServiceXmlSchema
        {
            get { return restServiceXmlSchema; }
            set { restServiceXmlSchema = value; }
        }

        /// <summary>
        /// The service Uri for the current service
        /// </summary>
        public Uri ServiceUri
        {
            get { return serviceUrl; }
            set { serviceUrl = value; }
        }
        #endregion

        /// <summary>
        /// Throws a RestServiceException based on the supplied string
        /// </summary>
        /// <exception cref="RestServiceException" />
        private void ThrowRestServiceException(String message)
        {
            throw new RestServiceException(message);
        }

        /// <summary>
        /// Throws a RestServiceException based on the supplied string and
        /// inner exception
        /// </summary>
        /// <exception cref="RestServiceException" />
        private void ThrowRestServiceException(String message, Exception innerException)
        {
            throw new RestServiceException(message, innerException);
        }

        /// <summary>
        /// ctor
        /// </summary>
        public RestServiceProxyBase()
        {
            this.restServiceXmlSchema = new DefaultRestServiceXmlSchema();
        }

        /// <summary>
        /// ctor : RestServiceXmlSchema
        /// </summary>
        /// <param name="restServiceXmlSchema"></param>
        public RestServiceProxyBase(IRestServiceXmlSchema restServiceXmlSchema)
        {
            this.restServiceXmlSchema = restServiceXmlSchema;
        }

        /// <summary>
        /// Utility member for posting data to the supplied service URI
        /// </summary>
        /// <param name="serviceUri"></param>
        /// <param name="headers"></param>
        /// <param name="parameters"></param>
        /// <returns>The stream returned by the service</returns>
        public Stream Get(
            Uri serviceUri,
            WebHeaderCollection headers,
            RestParameterCollection parameters)
        {
            RestCommand command = new RestCommand(serviceUri, new GetRestTransport());

            if (headers != null)
            {
                command.Headers.Add(headers);
            }

            if (parameters != null)
            {
                command.Parameters.Add(parameters);
            }

            return command.GetResponseStream();
        }

        /// <summary>
        /// Invokes the supplied command.
        /// TODO: (BJB, 26-10-2006) The idea is to centralise production of RestServiceException. 
        /// We really want to make it easy to use this one. 
        /// The only way to enforce this better is to do it at
        /// command level, otherwise developer can bypass this checking step and may get
        /// results they do not understand. Perhaps rethink.
        /// 
        /// TODO: What if we know the Response will not be Xml? 
        /// This will always fail in that case. 
        /// Should this be converted to XmlRestServiceProxyBase?
        /// </summary>
        /// <param name="restCommand"></param>
        /// <returns></returns>
        /// <exception cref="RestServiceException">
        /// Thrown whenever a service responds to request with an exception message
        /// </exception>
        /// <exception cref="XmlException" />
        protected XmlDocument Invoke(RestCommand restCommand)
        {
            // Execute command
            XmlDocument responseXmlDocument = restCommand.ExecuteXml();

            // Validate Response and throw RestServiceException if found
            if (RestServiceXmlSchema.IsErrorResponse(responseXmlDocument))
            {
                ThrowRestServiceException(
                    RestServiceXmlSchema.GetExceptionMessage(responseXmlDocument));
            }

            return responseXmlDocument;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected String InvokeText(RestCommand restCommand)
        { 
            using (StreamReader reader = restCommand.ExecuteStreamReader())
            using (StringWriter writer = new StringWriter())
            {
                while (reader.Peek() > 0)
                {
                    writer.WriteLine(reader.ReadLine()); 
                }

                return writer.GetStringBuilder().ToString();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected HttpWebResponse GetResponse(RestCommand restCommand) 
        {
            return restCommand.Execute(); 
        }

        /// <summary>
        /// Utility member for posting data to the supplied service URI
        /// </summary>
        /// <param name="serviceUri"></param>
        /// <param name="headers"></param>
        /// <param name="parameters"></param>
        /// <param name="data"></param>
        /// <param name="httpContentType"></param>
        /// <returns>The stream returned by the service</returns>
        public Stream Post(Uri serviceUri, System.Net.WebHeaderCollection headers,
            RestParameterCollection parameters,
            RestDataItemCollection data, Rest.Http.HttpContentType httpContentType)
        {
            RestCommand command = new RestCommand(serviceUri, new PostRestTransport());
            command.Transport.HttpContentType = httpContentType;
            command.Headers.Add(headers);
            command.Parameters.Add(parameters);
            command.Data.Add(data);

            return command.GetResponseStream();
        }       
    }
}
